package org.simtube.lsi;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

import org.simtube.index.Region;
import org.simtube.util.DocItem;
import org.simtube.util.DocVector;

public class LSIConvertor {
	
	private double[][] u;
	private double[] s;
	private double[][] v;
	
	private int k;
	private int m;
	private int n;
	
	public LSIConvertor() {
	}
	
	/**
	 * Load SVD matrix [U, S, V] from the specified files
	 * 
	 * @param ufile The file containing U
	 * @param sfile The file containing S
	 * @param vfile The file containing V
	 * @param folder The original file folder
	 * @throws FileNotFoundException
	 */
	public void loadSVD(String ufile, String sfile, String vfile)
		throws FileNotFoundException {
		Scanner scan = new Scanner(new File(ufile));
		
		// the first line should be 'm k' which means the U matrix is m by k
		m = scan.nextInt();
		k = scan.nextInt();
		
		u = new double[m][k];
		for(int i = 0; i < m; i++)
			for(int j = 0; j < k; j++)
				u[i][j] = scan.nextDouble();
		
		scan.close();
		scan = new Scanner(new File(sfile));
		
		// the first line should be 'k' which means the S matrix has only k elements
		if(scan.nextInt() != k) {
			System.err.println("Matrix format error in LSIConvertor.java");
			return;
		}
		
		// actually we can only record k elements because s is diagonal
		s = new double[k];
		for(int i = 0; i < k; i++)
			s[i] = scan.nextDouble();
		
		scan.close();
		scan = new Scanner(new File(vfile));
		
		// the first line should be 'n k' which means the V matrix is n by k
		n = scan.nextInt();
		if(scan.nextInt() != k) {
			System.err.println("Matrix format error in LSIConvertor.java");
			return;
		}
		
		v = new double[n][k];
		for(int i = 0; i < n; i++)
			for(int j = 0; j < k; j++)
				v[i][j] = scan.nextDouble();
		scan.close();
	}
	
	/**
	 * Calculate the folded in vector for the given query
	 * 
	 * @param query A given query vector, the length of which should be the same
	 * as the length of an original document vector. 
	 * @return A folded in document vector, the length of which is k
	 */
	public DocVector foldIn(double[] query) {
		
		if(query.length != m) {
			System.out.println("Query length error in LSIConvertor.java");
			return null;
		}
		
		double[] dv = new double[k];
		double temp = 0;
		for(int i = 0; i < k; i++) {
			temp = 0;
			for(int j = 0; j < m; j++)
				temp += query[j] * u[j][i];
			dv[i] = temp;
		}
		
		return new DocVector(dv).normalize();
	}
	
	/**
	 * Generate a document matrix in the latent semantic space
	 * 
	 * @param dataFolder The folder containing all original documents
	 * @return The generated document matrix
	 */
	public DocItem[] generateDocMat(String dataFolder) {
		
		File f = new File(dataFolder);
		
		File[] flist = f.listFiles();
		
		DocItem[] docMat = new DocItem[n];
		double[] dv = null;
		
		for(int i = 0; i < n; i++) {
			dv = new double[k];
			for(int j = 0; j < k; j++)
				dv[j] = v[i][j] * s[j];
			docMat[i] = new DocItem(new DocVector(dv).normalize(),
					Integer.parseInt(flist[i].getName()));
			DocVector ndv = new DocVector(dv).normalize();
			System.out.println("DocMat line " + (i + 1) + ": "
					+ Region.distance(new Region(ndv, 0), new Region(ndv, 0)));
		}
		
		return docMat;
	}
	
	public static void main(String[] args) throws FileNotFoundException {
		LSIConvertor conv = new LSIConvertor();
		
		long start = System.nanoTime();
		
		conv.loadSVD("data/ufile", "data/sfile", "data/vfile");
		
		long end = System.nanoTime();
		System.out.println("Time loading matrix file: "
				+ ((end - start) / 1000000) + "ms");
		
		start = System.nanoTime();
		
		DocItem[] docMat = conv.generateDocMat("data/text");
		
		end = System.nanoTime();
		System.out.println("Time generating doc matrix: "
				+ ((end - start) / 1000000) + "ms");
		
		System.out.println("Number of rows: " + docMat.length);
	}
}
